package com.feiwo.nonego.rpc.common.scanner;

import com.feiwo.nonego.rpc.common.constants.CommonConstants;
import com.feiwo.nonego.rpc.common.util.ClassUtils;
import com.feiwo.nonego.rpc.common.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.jar.JarFile;

/**
 * 类扫描器
 *
 * @author feiwo 2025-08-03
 */
public class ClassScanner {

    private static final Logger LOGGER = LoggerFactory.getLogger(ClassScanner.class);

    /**
     * 通过文件系统扫描指定包路径下的所有类文件
     *
     * @param packageName   包名，用于构建完整的类名
     * @param packagePath   包对应的文件系统路径
     * @param classNameList 用于存储找到的类名列表
     */
    private static void findClassesInPackageByFile(String packageName, String packagePath, List<String> classNameList) {
        // 参数校验
        if (StringUtils.isEmpty(packageName) || StringUtils.isEmpty(packagePath) || Objects.isNull(classNameList)) {
            return;
        }

        var dir = new File(packagePath);
        // 如果文件不存在，或者不是目录，则直接返回
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }

        // 获取目录下所有子目录和.class文件
        var dirFiles = dir.listFiles(filePath ->
                filePath.isDirectory() || filePath.getName().endsWith(CommonConstants.CLASS_FILE_SUFFIX)
        );

        if (Objects.isNull(dirFiles)) {
            return;
        }

        // 递归遍历所有文件和目录
        for (File file : dirFiles) {
            if (file.isDirectory()) {
                // 如果是目录，递归查找
                findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), classNameList);
            } else {
                // 如果是.class文件，添加到类名列表中
                classNameList.add(packageName + "." + ClassUtils.getClassName(file.getName()));
            }
        }
    }

    /**
     * 从JAR文件中查找指定包下的所有类
     *
     * @param packageName    包名
     * @param packageDirName 包目录名
     * @param url            JAR文件的URL地址
     * @param classNameList  用于存储找到的类名列表
     * @throws IOException 当读取JAR文件出现IO异常时抛出
     */
    private static void findClassesInPackageByJar(String packageName, String packageDirName, URL url, List<String> classNameList) throws IOException {
        if (StringUtils.isEmpty(packageName) || StringUtils.isEmpty(packageDirName) || Objects.isNull(url) || Objects.isNull(classNameList)) {
            return;
        }

        JarFile jarFile;
        try {
            jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
        } catch (ClassCastException e) {
            // 非法的URL连接类型
            return;
        }

        if (Objects.isNull(jarFile)) {
            return;
        }

        try (jarFile) { // 自动关闭资源
            var entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                var entry = entries.nextElement();
                var name = entry.getName();

                if (StringUtils.isEmpty(name) || name.isEmpty()) {
                    continue;
                }

                if (name.charAt(0) == '/') {
                    name = name.substring(1);
                }

                // 判断条目是否属于指定包路径
                if (!name.startsWith(packageDirName)) {
                    continue;
                }

                int idx = name.lastIndexOf("/");
                if (idx == -1) {
                    continue;
                }

                packageName = name.substring(0, idx).replace("/", ".");

                // 如果是class文件且不是目录，则添加到类名列表中
                if (name.endsWith(CommonConstants.CLASS_FILE_SUFFIX) && !entry.isDirectory()) {
                    classNameList.add(packageName + "." + ClassUtils.getClassName(name));
                } else {
                    if (name.endsWith("/")) {
                        // 如果是目录，则递归查找
                        findClassesInPackageByJar(packageName, name, url, classNameList);
                    }
                }
            }
        }
    }

    /**
     * 获取指定包名下的所有类名列表
     *
     * @param packageName 包名，如"com.example.package"
     * @return 包含所有类名的字符串列表
     * @throws IOException 当读取资源时发生IO异常
     */
    public static List<String> getClassNameList(String packageName) throws IOException {
        var classNameList = new ArrayList<String>();
        var packageDirName = packageName.replace(".", "/");

        // 获取当前线程上下文类加载器中指定包路径的所有资源
        var dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
        while (dirs.hasMoreElements()) {
            var url = dirs.nextElement();
            if (Objects.isNull(url)) {
                continue;
            }

            try {
                // 根据不同的协议类型处理类文件的查找
                switch (url.getProtocol()) {
                    case CommonConstants.PROTOCOL_FILE -> {
                        // 正确解码文件路径，避免重复编码
                        var filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
                        findClassesInPackageByFile(packageName, filePath, classNameList);
                    }
                    case CommonConstants.PROTOCOL_JAR ->
                            findClassesInPackageByJar(packageName, packageDirName, url, classNameList);
                }
            } catch (Exception e) {
                LOGGER.error("获取类名列表异常：{}", e.getMessage());
            }
        }
        return classNameList;
    }


}
